﻿using AudioVideoConversation;
using Microsoft.Lync.Model;
using Microsoft.Lync.Model.Conversation;
using Microsoft.Lync.Model.Conversation.AudioVideo;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormDemo
{
    public partial class ConversationWindow : Form
    {
        #region "グローバル変数"

        //Lyncクライアント
        private LyncClient client;

        //カンバセーション
        private Conversation conversation;

        //参加者の AvModality
        private AVModality avModality;

        //Lyncオーディオチャネル
        private AudioChannel audioChannel;
        
        #endregion

        #region "コンストラクタ"

        /// <summary>
        /// フォームロード
        /// </summary>
        public ConversationWindow(Conversation conversation, LyncClient client)
        {
            InitializeComponent();

            //クライアント参照保存
            this.client = client;

            //カンバセーション参照保存
            this.conversation = conversation;

            //AVModality, AudioChannel and VideoChannel保存
            avModality = (AVModality)conversation.Modalities[ModalityTypes.AudioVideo];
            audioChannel = avModality.AudioChannel;
            
            //現在のカンバセーションとモダリティ状況を表示する
            toolStripStatusLabelConvesation.Text = conversation.State.ToString();
            toolStripStatusLabelModality.Text = avModality.State.ToString();

            //UI Suppressionモードによりチェックボックスを有効、無効にする
            //詳しくはcheckBoxAutoTerminateOnIdle_CheckStateChanged()メッソドに参照
            //checkBoxAutoTerminateOnIdle.Enabled = client.InSuppressedMode;

            //カンバセーション状況
            conversation.StateChanged += conversation_StateChanged;

            //カンバセーション参加者のイベント
            conversation.ParticipantAdded += conversation_ParticipantAdded;
            
            //モダリティアクション変更イベント （DTMF以外のオーディオボタンイベント)
            avModality.ActionAvailabilityChanged += avModality_ActionAvailabilityChanged;

            //モダリティ状況変更イベント
            avModality.ModalityStateChanged += avModality_ModalityStateChanged;

            //オーディオチャンネル状況変更イベント
            audioChannel.StateChanged += audioChannel_StateChanged;
        }

        #endregion

        #region "フォーム閉じる"

        /// <summary>
        /// フォームを閉じる時、カンバセーションを終了する
        /// </summary>
        private void ConversationWindow_FormClosing(object sender, FormClosingEventArgs e)
        {
            //フォーム閉じる時、イベントを削除
            conversation.StateChanged -= conversation_StateChanged;
            conversation.ParticipantAdded -= conversation_ParticipantAdded;
            avModality.ActionAvailabilityChanged -= avModality_ActionAvailabilityChanged;
            avModality.ModalityStateChanged -= avModality_ModalityStateChanged;
            audioChannel.StateChanged -= audioChannel_StateChanged;
            
            if (conversation.State != ConversationState.Terminated)
            {
                //カンバセーションを終了する
                try
                {
                    conversation.End();
                }
                catch (LyncClientException lyncClientException)
                {
                    Console.WriteLine(lyncClientException);
                }
                catch (SystemException systemException)
                {
                    if (LyncModelExceptionHelper.IsLyncException(systemException))
                    {
                        // Lync Model APIシステムエクセプション
                        Console.WriteLine("エラー: " + systemException);
                    }
                    else
                    {
                        //Lync Model APIのシステムエラー以外のエラー場合、Rethrowする
                        throw;
                    }
                }
            }
        }
        #endregion

        #region "カンバセーションイベントハンドラ"

        /// <summary>
        /// カンバセーション状況変更イベント
        /// 
        /// </summary>
        void conversation_StateChanged(object sender, ConversationStateChangedEventArgs e)
        {
            //UIスレッドに実行をポスト
            this.BeginInvoke(new MethodInvoker(delegate()
            {
                //現在のカンバセーション状況を表示する
                toolStripStatusLabelConvesation.Text = e.NewState.ToString();
            }));
        }

        /// <summary>
        /// カンバセーションメンバーを追加するイベント
        /// 
        /// </summary>
        void conversation_ParticipantAdded(object sender, ParticipantCollectionChangedEventArgs e)
        {
            //カンバセーションメンバー追加エラー
            if (e.StatusCode < 0)
            {
                Console.Out.WriteLine("カンバセーションメンバーを追加できません。: コード=" + e.StatusCode);
                return;
            }

            //UIスレッドに実行をポスト
            this.BeginInvoke(new MethodInvoker(delegate()
            {
                //リストボックスにカンバセーションメンバー追加
                listBoxRosterContacts.Items.Add(new ParticipantItem(e.Participant));
            }));

        }

        #endregion

        #region "モダリティアクション"

        /// <summary>
        /// 「Lync通話」ボタンイベント: AvModality.BeginConnect()
        /// </summary>
        private void buttonConnectAudio_Click(object sender, EventArgs e)
        {
            //オーディオカンバセーション開始する
            try
            {
                AsyncCallback callback = new AsyncOperationHandler(avModality.EndConnect).Callback;
                avModality.BeginConnect(callback, null);
            }
            catch (LyncClientException lyncClientException)
            {
                Console.WriteLine(lyncClientException);
            }
            catch (SystemException systemException)
            {
                if (LyncModelExceptionHelper.IsLyncException(systemException))
                {
                    MessageBox.Show("オーディオカンバセーション開始する時に、エラーが発生しました。"+systemException);
                    Console.WriteLine("エラー:: " + systemException);
                }
                else
                {
                    throw;
                }
            }
        }

        /// <summary>
        /// 「切断」ボタンイベント: AvModality.BeginDisconnect()
        /// </summary>
        private void buttonDisconnectAudio_Click(object sender, EventArgs e)
        {
            //オーディオカンバセーションを終了する
            try
            {
                AsyncCallback callback = new AsyncOperationHandler(avModality.EndDisconnect).Callback;
                avModality.BeginDisconnect(ModalityDisconnectReason.None, callback, null);
            }
            catch (LyncClientException lyncClientException)
            {
                Console.WriteLine(lyncClientException);
            }
            catch (SystemException systemException)
            {
                if (LyncModelExceptionHelper.IsLyncException(systemException))
                {
                    MessageBox.Show("オーディオカンバセーション切断する時に、エラーが発生しました。" + systemException);
                    Console.WriteLine("エラー: " + systemException);
                }
                else
                {
                    throw;
                }
            }
        }

        /// <summary>
        /// 「保留」ボタンイベント: AvModality.BeginHold()
        /// </summary>
        private void buttonHold_Click(object sender, EventArgs e)
        {
            //カンバセーションを保留する
            AsyncCallback callback = new AsyncOperationHandler(avModality.EndHold).Callback;
            try
            {
                avModality.BeginHold(callback, null);
            }
            catch (LyncClientException lyncClientException)
            {
                Console.WriteLine(lyncClientException);
            }
            catch (SystemException systemException)
            {
                if (LyncModelExceptionHelper.IsLyncException(systemException))
                {
                    MessageBox.Show("オーディオカンバセーション保留する時に、エラーが発生しました。" + systemException);
                    Console.WriteLine("エラー:: " + systemException);
                }
                else
                {
                    throw;
                }
            }
        }

        /// <summary>
        /// 「応答」ボタンイベント: AvModality.BeginRetrieve()
        /// </summary>
        private void buttonRetrieve_Click(object sender, EventArgs e)
        {
            //保留した状況を削除する
            AsyncCallback callback = new AsyncOperationHandler(avModality.EndRetrieve).Callback;
            try
            {
                avModality.BeginRetrieve(callback, null);
            }
            catch (LyncClientException lyncClientException)
            {
                Console.WriteLine(lyncClientException);
            }
            catch (SystemException systemException)
            {
                if (LyncModelExceptionHelper.IsLyncException(systemException))
                {
                    MessageBox.Show("オーディオカンバセーション応答時に、エラーが発生しました。" + systemException);
                    Console.WriteLine("エラー:: " + systemException);
                }
                else
                {
                    throw;
                }
            }
        }

        /// <summary>
        /// 「承諾」ボタンイベント: AvModality.Accept()
        /// </summary>
        private void buttonAccept_Click(object sender, EventArgs e)
        {
            //オーディオカンバセーションを承諾する (syncronous operation)
            try
            {
                avModality.Accept();
            }
            catch (LyncClientException lyncClientException)
            {
                Console.WriteLine(lyncClientException);
            }
            catch (SystemException systemException)
            {
                if (LyncModelExceptionHelper.IsLyncException(systemException))
                {
                    MessageBox.Show("オーディオカンバセーション承諾時に、エラーが発生しました。" + systemException);
                    Console.WriteLine("エラー: " + systemException);
                }
                else
                {
                    throw;
                }
            }
        }

        /// <summary>
        /// 「無視」ボタンイベント: AvModality.Reject()
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void buttonReject_Click(object sender, EventArgs e)
        {
            //オーディオカンバセーションを無視する
            try
            {
                avModality.Reject(ModalityDisconnectReason.None);
            }
            catch (LyncClientException lyncClientException)
            {
                Console.WriteLine(lyncClientException);
            }
            catch (SystemException systemException)
            {
                if (LyncModelExceptionHelper.IsLyncException(systemException))
                {
                    MessageBox.Show("オーディオカンバセーション無視時に、エラーが発生しました。" + systemException);
                    Console.WriteLine("エラー: " + systemException);
                }
                else
                {
                    // Rethrow the SystemException which did not come from the Lync Model API.
                    throw;
                }
            }
        }

        #endregion

        #region "モダリティイベントハンドラ"

        /// <summary>
        /// モダリティ状況変更イベント
        /// 
        /// </summary>
        void avModality_ModalityStateChanged(object sender, ModalityStateChangedEventArgs e)
        {
            //UIスレッドに実行をポスト
            this.BeginInvoke(new MethodInvoker(delegate()
            {
                //現在のカンバセーション状況を表示する
                toolStripStatusLabelModality.Text = e.NewState.ToString();
            }));

        }

        /// <summary>
        /// モダリティアクション変更イベント
        /// 
        /// </summary>
        void avModality_ActionAvailabilityChanged(object sender, ModalityActionAvailabilityChangedEventArgs e)
        {
            //UIスレッドに実行をポスト
            this.BeginInvoke(new MethodInvoker(delegate()
            {

                //ボタンの有効、無効
                switch (e.Action)
                {
                    case ModalityAction.Connect:
                        buttonConnectAudio.Enabled = e.IsAvailable;
                        break;

                    case ModalityAction.Disconnect:
                        buttonDisconnectAudio.Enabled = e.IsAvailable;
                        break;

                    case ModalityAction.Hold:
                        buttonHold.Enabled = e.IsAvailable;
                        break;

                    case ModalityAction.Retrieve:
                        buttonRetrieve.Enabled = e.IsAvailable;
                        break;

                    case ModalityAction.Accept:
                        buttonAccept.Enabled = e.IsAvailable;
                        break;

                    case ModalityAction.Reject:
                        buttonReject.Enabled = e.IsAvailable;
                        break;
                }

            }));
        }


        #endregion

        #region "オーディオチャンネルイベントハンドラ"

        /// <summary>
        /// オーディオチャネル状況変更イベント
        /// </summary>
        void audioChannel_StateChanged(object sender, ChannelStateChangedEventArgs e)
        {
            //UIスレッドに実行をポスト
            this.BeginInvoke(new MethodInvoker(delegate()
            {
                //現在のオーディオチャネル状況を表示する
                toolStripStatusLabelAudioChannel.Text = e.NewState.ToString();
            }));
        }

        #endregion

        #region "Asyncイベントハンドラ"
        /// <summary>
        /// Asynchronous操作によってスローされた例外を公開するヘルパークラス
        /// </summary>
        class AsyncOperationHandler
        {
            private Action<IAsyncResult> endOperation;
            public AsyncOperationHandler(Action<IAsyncResult> endOperation)
            {
                this.endOperation = endOperation;
            }

            public void Callback(IAsyncResult ar)
            {
                try
                {
                    // Async operations例外.
                    endOperation(ar);
                }
                catch (Exception e)
                {
                    Console.Out.WriteLine(e);
                }
            }
        }
        #endregion
    }
}
